/**
 * Author: ZhangYang
 * Version: 1.01
 * Usage:
 * 1. loadValidateRules(ruleFile) or loadValidateRules(ruleFilePath);
 * 2. if return true: getTemplateValidateRule(), getHeadValidateRuleMap(), getDetailValidateRuleMap();
 *    if return false: getErrorList()
 */

package com.pccw.portlet.util.excel.exportutil;

import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFComment;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;

import com.pccw.portlet.util.PccwCommonUtil;
import com.pccw.portlet.util.excel.ExcelUtil;

public class ExportRuleExcelLoader implements ExportRuleLoader {
	private HSSFWorkbook workbook = null;
	private List errorList = new ArrayList();
	private Map headDataRuleMap = new HashMap();
	private Map detailDataRuleMap = new HashMap();
	private TemplateExportRule templateExportRule = new TemplateExportRule();
	
	/**
	 * Empty Constructor
	 */
	public ExportRuleExcelLoader(){
		
	}
	
	/**
	 * ���ָ���ĵ�������ļ��ļ�·�������ص�������
	 */
	public boolean loadExportRules(String ruleFilePath) {
		File ruleFile = null;
		try{
			ruleFile = new File(ruleFilePath);
		}
		catch(Exception e){
			e.printStackTrace();
		}
		
		if(ruleFile != null){
			return loadExportRules(ruleFile);
		}
		else{
			return false;
		}
	}

	/**
	 * ���ָ���ĵ�������ļ��ļ����������ص�������
	 */
	public boolean loadExportRules(File ruleFile) {
		if(ruleFile == null){
			return false;
		}
		
		FileInputStream fis = null;
		try {
			fis = new FileInputStream(ruleFile);
			this.workbook = new HSSFWorkbook(fis);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (fis != null)
					fis.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		if(this.workbook == null){
			this.errorList.add("The template file \"" + ruleFile.getAbsolutePath() + "\" is not Excel file or contains unreadable format!");
			return false;
		}
		
		int sheetCount = this.workbook.getNumberOfSheets();
		if(sheetCount <= 1){
			// ����EXCEL�ļ���ֻ��һ��Sheet������Ϊ�䲻����ĵ�������ļ���
			// ��Ϊ��ĵ�������ļ�����Ҫ������Sheet����һ�������е�Ԫ��ĵ������ã��ڶ��������ģ�����������
			this.errorList.add("There is no enough sheets in Excel template file!");
			return false;
		}
		
		boolean result = true;
		// �������ģ���ȫ�����ã���Excel��������ļ��ĵڶ���sheet�ж�ȡ��
		if(this.loadTemplateExportRules() == false){
			result = false;
		}
		
		// �����赼����sheet�����е�Ԫ��ĵ������򣨴�Excel��������ļ��ĵ�һ��sheet�ж�ȡ��
		if(this.loadCellExportRules() == false){
			result = false;
		}
		
		return result;
	}
	
	/**
	 * �������ģ��ĵ�������ȫ�����ã�ȫ����ֵ��TemplateExportRule�������Ӧ����
	 * ���̶��ӵڶ���sheet�ж�ȡ��
	 */
	private boolean loadTemplateExportRules(){
		HSSFSheet sheet = this.workbook.getSheetAt(1); // �̶��ӵڶ���sheet�ж�ȡ���ģ���ļ���ȫ������
		HSSFRow row = null;
		HSSFCell cell = null;
		boolean result = true;
		
		for(int i = 0; i <= sheet.getLastRowNum(); i++){
			String ruleString = null;
			String key = null;
			String value = null;
			
			row = sheet.getRow(i);
			if(row == null){
				continue;
			}
			
			cell = row.getCell((short)0); // �̶��ӵ�һ����Ԫ���ж�ȡ����
			if(cell == null){
				continue;
			}
			
			if(cell.getCellType() == HSSFCell.CELL_TYPE_STRING){
				if(cell.getRichStringCellValue() != null){
					ruleString = cell.getRichStringCellValue().getString();
				}
				
				if(ruleString != null){
					key = PccwCommonUtil.getPropertiesKey(ruleString);
					value = PccwCommonUtil.getPropertiesValue(ruleString);
				}
				
				if(key != null && !"".equals(key.trim()) && value != null && !"".equals(value.trim())){
					if(this.assignTemplateExportRule(key, value) == false){
						result = false;
					}
				}
			}
		}
		
		return result;
	}
	
	/**
	 * ����ĳ����������ȫ�����ã���ֵ��TemplateExportRule�������Ӧ����
	 */
	private boolean assignTemplateExportRule(String key, String value){
		if(key == null || "".equals(key.trim())){
			return false;
		}
		
		boolean result = true;
		boolean hasUnsupportedValue = false;
		
		if("is-multi-sheet".equalsIgnoreCase(key)){
			if("true".equalsIgnoreCase(value)){
				this.templateExportRule.setMultiSheet(true);
			}
			else if("false".equalsIgnoreCase(value)){
				this.templateExportRule.setMultiSheet(false);
			}
			else{
				hasUnsupportedValue = true;
			}
		}
		else if("has-head".equalsIgnoreCase(key)){
			if("true".equalsIgnoreCase(value)){
				this.templateExportRule.setHasHead(true);
			}
			else if("false".equalsIgnoreCase(value)){
				this.templateExportRule.setHasHead(false);
			}
			else{
				hasUnsupportedValue = true;
			}
		}
		else if("has-detail".equalsIgnoreCase(key)){
			if("true".equalsIgnoreCase(value)){
				this.templateExportRule.setHasDetail(true);
			}
			else if("false".equalsIgnoreCase(value)){
				this.templateExportRule.setHasDetail(false);
			}
			else{
				hasUnsupportedValue = true;
			}
		}
		else if("head-bean-class".equalsIgnoreCase(key)){
			this.templateExportRule.setHeadBeanClass(value);
		}
		else if("detail-layout-type".equalsIgnoreCase(key)){
			if("row".equalsIgnoreCase(value) || "column".equalsIgnoreCase(value)){
				this.templateExportRule.setDetailLayoutType(value);
			}
			else{
				hasUnsupportedValue = true;
			}
		}
		else if("detail-start-index".equalsIgnoreCase(key)){
			try{
				int startIndex = Integer.parseInt(value);
				this.templateExportRule.setDetailStartIndex(startIndex);
			}
			catch(Exception e){
				hasUnsupportedValue = true;
			}
		}
		else if("detail-bean-class".equalsIgnoreCase(key)){
			this.templateExportRule.setDetailBeanClass(value);
		}
		else{
			this.errorList.add("Unsupported rule key \"" + key + "\" in Excel template file!");
			result = false;
		}
		
		if(hasUnsupportedValue){
			this.errorList.add("Unsupported rule value \"" + value + "\" for rule key \"" + key + "\"");
			result = false;
		}

		return result;
	}
	
	/**
	 * ���ص�������ļ����赼��sheet�����е�Ԫ��ĵ�������ȫ��ת����CellExportRule������ӵ���Ӧ��RuleMap������
	 * �̶���Excel��������ļ��ĵ�һ��sheet�ж�ȡ
	 */
	private boolean loadCellExportRules(){
		boolean result = true;
		
		HSSFSheet sheet = this.workbook.getSheetAt(0); // �̶���Excel��������ļ��ĵ�һ��sheet�ж�ȡ��Ԫ��ĵ�������
		HSSFRow row = null;
		HSSFCell cell = null;
		
		// ѭ����ȡ��������ļ�Sheet�ĸ�����
		for(int rowId = 0; rowId <= sheet.getLastRowNum(); rowId++){
			//System.out.println("\n====================== reading cell rule from row:" + rowId);
			row = sheet.getRow(rowId);
			if(row == null){
				//System.out.println("row:" + rowId + " is null");
				continue;
			}
			
			// ѭ����ȡ���е����е�Ԫ��
			for(short columnId = 0; columnId < row.getLastCellNum(); columnId++){
				//System.out.println("reading cell rule from row:" + rowId + " column:" + columnId);
				cell = row.getCell(columnId);
				if(cell == null){
					//System.out.println("cell:" + columnId + " of row:" + rowId + " is null");
					continue;
				}
				
				if(this.loadCellExportRule(cell, rowId, columnId) == false){
					result = false;
				}
			}
		}
		
		return result;
	}
	
	/**
	 * ����һ����Ԫ��ĵ�������Ϊ�����ı����ݣ�����ת����CellExportRule������ӵ���Ӧ��RuleMap������ (*)
	 */
	private boolean loadCellExportRule(HSSFCell cell, int rowId, short columnId){
		// ��ȡ�õ�Ԫ�����ע���ݣ�Ϊ�õ�Ԫ��ĵ������ã�
		HSSFComment comment = cell.getCellComment();
		if(comment == null){
			return true;
		}
		
		String ruleParagraph = comment.getString().getString();
		
		if(ruleParagraph == null || "".equals(ruleParagraph.trim())){
			return true; // ���ù����ı��Σ�һ����Ԫ�����ע���ݣ�Ϊ�գ�����Ϊ��������Ԫ�����ע�����ǵ������ã����Բ���
		}
		
		boolean result = true;
		
		String ruleString = null;
		String key = null;
		String value = null;
		
		String[] ruleLines = ruleParagraph.split("\n");
		CellExportRule cellExportRule = new CellExportRule();
		
		for(int lineId = 0; lineId < ruleLines.length; lineId++){
			ruleString = ruleLines[lineId];
			if(ruleString != null){
				key = PccwCommonUtil.getPropertiesKey(ruleString);
				value = PccwCommonUtil.getPropertiesValue(ruleString);
			}
			
			if(key != null && !"".equals(key.trim()) && value != null && !"".equals(value.trim())){
				cellExportRule = this.assignCellExportRule(cellExportRule, key, value);
				if(cellExportRule == null){
					break;
				}
			}
		}
		
		// �жϷ�װ���Ժ��CellExportRule�Ƿ���������������ӵ���Ӧ��RuleMap��
		// Ŀǰ�Ȳ�����ϸ�жϣ��Ժ������� (*)
		if(cellExportRule == null){
			result = false;
		}
		else{
			cellExportRule.setRowId(rowId);
			cellExportRule.setColumnId(columnId);
			
			String cellType = cellExportRule.getCellType();
			if(cellType != null && ! "".equals(cellType)){
				if(CellExportRule.CELL_TYPE_OF_HEAD_DATA.equals(cellType)){
					this.headDataRuleMap.put(cellExportRule.getCellName(), cellExportRule);
				}
				else if(CellExportRule.CELL_TYPE_OF_DETAIL_DATA.equals(cellType)){
					this.detailDataRuleMap.put(cellExportRule.getCellName(), cellExportRule);
				}
			}
		}
		
		return result;
	}
	

	/**
	 * ����ĳ����������ȫ�����ã���ֵ��TemplateExportRule�������Ӧ����
	 * ��Щ�����value�Ƿ�������Ŀ�ѡ��Χ�ڻ�������ϸ�жϣ���ʱ��������(*)
	 */
	private CellExportRule assignCellExportRule(CellExportRule cellExportRule, String key, String value){
		if(key == null || "".equals(key.trim())){
			return cellExportRule;
		}
		
		boolean hasUnsupportedValue = false;
		
		if("name".equalsIgnoreCase(key)){
			if("".equals(value.trim())){
				this.errorList.add("Cell name can not be empty!");
				return null;
			}
			else{
				cellExportRule.setCellName(value);
			}
		}
		else if("title".equalsIgnoreCase(key)){
			cellExportRule.setCellTitle(value);
		}
		else if("row-id".equalsIgnoreCase(key)){
			try{
				int rowId = Integer.parseInt(value);
				if(rowId < 0 || rowId > ExcelUtil.MAX_ROW_ID_IN_SHEET){
					hasUnsupportedValue = true;
				}
				else{
					cellExportRule.setRowId(rowId);
				}
			}
			catch(Exception e){
				hasUnsupportedValue = true;
			}
		}
		else if("column-id".equalsIgnoreCase(key)){
			try{
				short columnId = Short.parseShort(value);
				if(columnId < 0 || columnId > ExcelUtil.MAX_COLUMN_ID_IN_SHEET){
					hasUnsupportedValue = true;
				}
				else{
					cellExportRule.setColumnId(columnId);
				}
			}
			catch(Exception e){
				hasUnsupportedValue = true;
			}
		}
		else if("cell-type".equalsIgnoreCase(key)){
			if(CellExportRule.isSupportedCellType(value)){
				cellExportRule.setCellType(value);
			}
			else{
				hasUnsupportedValue = true;
			}
		}
		else if("cell-format".equalsIgnoreCase(key)){
			if(CellExportRule.isSupportedCellFormat(value)){
				cellExportRule.setCellFormat(value);
			}
			else{
				hasUnsupportedValue = true;
			}
		}
		else if("valid-range".equalsIgnoreCase(key)){
			// isSupported ??? (*)
			cellExportRule.setValidRange(value);
		}
		else{
			this.errorList.add("Unsupported rule key \"" + key + "\" in Excel template file!");
			cellExportRule = null;
		}
		
		if(hasUnsupportedValue){
			this.errorList.add("Unsupported rule value \"" + value + "\" for rule key \"" + key + "\"");
			cellExportRule = null;
		}
		
		/*
		if(cellExportRule != null){
			System.out.println(key + " = " + value); // (*)
		}
		*/
		
		return cellExportRule;
	}

	/**
	 * ��������ͷ��Ϣ��ݵĵ�������
	 */
	public Map getHeadDataRuleMap() {
		return this.headDataRuleMap;
	}

	/**
	 * ����������ϸ��Ϣ��ݵĵ�������
	 */
	public Map getDetailDataRuleMap() {
		return this.detailDataRuleMap;
	}

	/**
	 * ���ص�������ģ���ȫ������
	 */
	public TemplateExportRule getTemplateExportRule() {
		return this.templateExportRule;
	}

	/**
	 * ���ؼ��ص����������в���Ĵ�����Ϣ
	 */
	public List getErrorList() {
		return this.errorList;
	}
	
	/**
	 * ����Excel��������ļ���workbook����
	 */
	public HSSFWorkbook getWorkBook(){
		return this.workbook;
	}
}
